package api

import (
	"github.com/satori/go.uuid"
	"moss/meta"
	"moss/proto"
	"moss/status"
	"os"
	"time"
)

func PutBucketLifecycle(requestId string, accessKeyId string, bucketName string, rule proto.Rule) status.Status {
	if !validIdentifier(bucketName) {
		return status.InvalidBucketName
	}

	bucketMeta, ok := meta.GetBucketInfo(bucketName)
	if !ok {
		return status.NoSuchBucket
	}

	if bucketMeta.OwnerAccessKeyId != accessKeyId {
		return status.AccessDenied
	}

	ruleId := rule.ID
	if len(ruleId) == 0 {
		randomId, err := uuid.NewV4()
		if err != nil {
			return status.InternalError
		}
		ruleId = randomId.String()
	}

	var enabled bool
	if rule.Status == "Enabled" {
		enabled = true
	} else if rule.Status == "Disabled" {
		enabled = false
	} else {
		return status.InvalidArgument
	}

	expireDays := rule.Expiration.Days
	expireCreateBefore := rule.Expiration.CreatedBeforeDate
	abortMUDays := rule.AbortMultipartUpload.Days
	abortMUCreateBefore := rule.AbortMultipartUpload.CreatedBeforeDate

	expireBoth   := expireDays != 0 && len(expireCreateBefore) != 0
	expireFound  := expireDays != 0 || len(expireCreateBefore) != 0
	abortMUBoth  := abortMUDays != 0 && len(abortMUCreateBefore) != 0
	abortMUFound := abortMUDays != 0 || len(abortMUCreateBefore) != 0

	if expireBoth || abortMUBoth || (!expireFound && !abortMUFound) {
		return status.InvalidArgument
	}

	if len(expireCreateBefore) != 0 {
		_, err := time.Parse(time.RFC3339, expireCreateBefore)
		if err != nil {
			return status.InvalidArgument
		}
	}

	if len(abortMUCreateBefore) != 0 {
		_, err := time.Parse(time.RFC3339, abortMUCreateBefore)
		if err != nil {
			return status.InvalidArgument
		}
	}

	meta.SetLifeCycleRule(bucketName, accessKeyId, ruleId, rule.Prefix, enabled, expireDays, expireCreateBefore, abortMUDays, abortMUCreateBefore)
	return status.Success
}

func GetBucketLifecycle(requestId string, accessKeyId string, bucketName string) (*proto.LifecycleConfiguration, status.Status) {
	if !validIdentifier(bucketName) {
		return nil, status.InvalidBucketName
	}

	bucketMeta, ok := meta.GetBucketInfo(bucketName)
	if !ok {
		return nil, status.NoSuchBucket
	}

	if bucketMeta.OwnerAccessKeyId != accessKeyId {
		return nil, status.AccessDenied
	}

	var xmlDoc proto.LifecycleConfiguration
	var rule proto.Rule
	lifeCycle, ok := meta.GetLifeCycleByBucket(bucketName)
	if !ok {
		return nil, status.NoSuchBucket
	}

	for i := 0; i < len(lifeCycle); i++ {
		rule.ID = lifeCycle[i].ID
		rule.Prefix = lifeCycle[i].Prefix
		rule.Expiration.Days = lifeCycle[i].Days
		rule.Expiration.CreatedBeforeDate = lifeCycle[i].CreatedBeforeDate
		rule.AbortMultipartUpload.Days = lifeCycle[i].AbortDays
		rule.AbortMultipartUpload.CreatedBeforeDate = lifeCycle[i].AbortCreatedBeforeData
		xmlDoc.Rule = append(xmlDoc.Rule, rule)
	}

	return &xmlDoc, status.Success
}

func DeleteBucketLifecycle(requestId string, accessKeyId string, bucketName string) status.Status {
	if !validIdentifier(bucketName) {
		return status.InvalidBucketName
	}

	bucketMeta, ok := meta.GetBucketInfo(bucketName)
	if !ok {
		return status.NoSuchBucket
	}

	if bucketMeta.OwnerAccessKeyId != accessKeyId {
		return status.AccessDenied
	}

	_, ok = meta.GetLifeCycleByBucket(bucketName)
	if !ok {
		return status.NoSuchLifeCycle
	}

	ok = meta.DeleteLifeCycle(bucketName)
	if !ok {
		return status.NoSuchBucket
	}

	return status.Success
}

func LifeCycleRoutine() {
	ruleList, ok := meta.GetAllLifeCycle()
	if !ok {
		return
	}

	for _, v := range ruleList {
		go func() {
			bucketName := v.BucketName
			prefix := v.Prefix

			// Expiration
			beforeDate, _ := time.Parse(time.RFC3339, v.CreatedBeforeDate)
			createTime, _ := time.Parse(time.RFC3339, v.CreateTime)
			days := v.Days
			objectList := meta.GetAllObjects(bucketName, prefix)

			for _, object := range objectList {
				if days > 0 {
					if time.Now().Sub(createTime).Hours() > float64(days*24) {
						meta.DeleteObject(bucketName, object.Key)
						os.Remove(object.FileLocation)
					}
				} else if len(v.CreatedBeforeDate) != 0 {
					if time.Now().After(beforeDate) {
						meta.DeleteObject(bucketName, object.Key)
						os.Remove(object.FileLocation)
					}
				}

			}

			// AbortMultipartUpload
			// TODO
		}()
	}
}
